package com.fw.service.parentmerchant.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;

import com.fw.service.applicationlog.ApplicationLogService;
import com.fw.service.applicationlog.bean.ApplicationLogBean;
import com.fw.service.auditlog.AuditLogService;
import com.fw.service.auditlog.bean.AuditLogBean;
import com.fw.service.bean.EntryBean;
import com.fw.service.bean.ErrorBean;
import com.fw.service.bean.SearchCriteriaBean;
import com.fw.service.bean.SearchResultBean;
import com.fw.service.formvalidator.FormValidatorService;
import com.fw.service.merchantprofile.bean.MerchantProfileBean;
import com.fw.service.parentmerchant.ParentMerchantService;
import com.fw.service.parentmerchant.bean.ParentMerchantBean;
import com.fw.service.parentmerchant.bean.SearchParentMerchantBean;
import com.fw.service.persistence.MerchantProfilePersistenceService;
import com.fw.service.persistence.ParentMerchantPersistenceService;
import com.fw.service.persistence.dto.Error;
import com.fw.service.persistence.dto.MerchantProfile;
import com.fw.service.persistence.dto.ParentMerchant;
import com.fw.service.persistence.dto.ServiceProfile;
import com.fw.service.property.PropertyService;
import com.fw.util.DateUtils;
import com.fw.util.PageRequestUtils;
import com.fw.util.StringUtils;

public class ParentMerchantServiceImpl implements ParentMerchantService {

    private static Logger log = Logger.getLogger(ParentMerchantServiceImpl.class.getName());

    @Autowired
    private PropertyService propertyService;

    @Autowired
    private ApplicationLogService applicationLogService;

    @Autowired
    private AuditLogService auditLogService;

    @Autowired
    private FormValidatorService formValidatorService;

    @Autowired
    private MerchantProfilePersistenceService merchantProfilePersistenceService;

    @Autowired
    private ParentMerchantPersistenceService parentMerchantPersistenceService;

    @Override
    public List<EntryBean<String, Serializable>> createParentMerchant(ParentMerchantBean parentMerchantBean) {
	String clazz = ParentMerchantService.class.getName();
	String method = "createParentMerchant";
	List<EntryBean<String, Serializable>> entryBeans = null;
	try {
	    ApplicationLogBean applicationLogBean = new ApplicationLogBean();
	    applicationLogBean.setType(ApplicationLogBean.TYPE_INFO);
	    applicationLogBean.setClazz(clazz);
	    applicationLogBean.setMethod(method);
	    applicationLogBean.setMessage(method);
	    applicationLogService.createApplicationLog(applicationLogBean);

	    if (parentMerchantBean != null) {
		entryBeans = new ArrayList<EntryBean<String, Serializable>>();
		// validate
		List<Error> errors = formValidatorService.validate(clazz, method, parentMerchantBean);
		List<ErrorBean> errorBeans = new ArrayList<ErrorBean>();
		if (errors != null && !errors.isEmpty()) {
		    for (Error error : errors) {
			ErrorBean errorBean = new ErrorBean();
			BeanUtils.copyProperties(error, errorBean);
			errorBeans.add(errorBean);
		    }
		}
		// validate unique
		if (!this.checkUniqueParentId(parentMerchantBean.getParentId())) {
		    ErrorBean errorBean = new ErrorBean();
		    errorBean.setErrorCode("errors.com.fw.service.parentmerchant.ParentMerchantService.createParentMerchant.parentIdExist");
		    errorBeans.add(errorBean);
		}
		if (errorBeans != null && !errorBeans.isEmpty()) {
		    entryBeans.add(new EntryBean<String, Serializable>(List.class.getName() + "." + ErrorBean.class.getName(),
			    (Serializable) errorBeans));
		} else {
		    Date currentDate = DateUtils.getCurrentDate();
		    String currentDateStr = StringUtils.formatDate(currentDate, StringUtils.PATTERN_DATETIME_PERSISTENCE);
		    // save
		    ParentMerchant parentMerchant = new ParentMerchant();
		    BeanUtils.copyProperties(parentMerchantBean, parentMerchant);
		    Set<MerchantProfileBean> merchantProfileBeans = parentMerchantBean.getMerchantProfileBeans();
		    if (merchantProfileBeans != null && !merchantProfileBeans.isEmpty()) {
			for (MerchantProfileBean merchantProfileBean : merchantProfileBeans) {
			    MerchantProfile merchantProfile = merchantProfilePersistenceService.findOne(merchantProfileBean.getId());
			    if (merchantProfile != null) {
				merchantProfile.getParentMerchants().add(parentMerchant);
				parentMerchant.getMerchantProfiles().add(merchantProfile);
			    }
			}
		    }
		    parentMerchant.setCreateBy(parentMerchantBean.getLoginUsername());
		    parentMerchant.setCreateDatetime(currentDateStr);
		    parentMerchant.setLastUpdateBy(parentMerchantBean.getLoginUsername());
		    parentMerchant.setLastUpdateDatetime(currentDateStr);
		    parentMerchant = parentMerchantPersistenceService.save(parentMerchant);

		    ParentMerchantBean resultParentMerchantBean = new ParentMerchantBean();
		    BeanUtils.copyProperties(parentMerchant, resultParentMerchantBean);
		    resultParentMerchantBean.setId(parentMerchant.getId());
		    // audit log
		    String logActionType = propertyService.getProperty("auditlog." + clazz + "." + method);
		    if (logActionType != null) {
			AuditLogBean auditLogBean = new AuditLogBean();
			auditLogBean.setLogActionType(logActionType);
			auditLogBean.setDescription("Parent ID: " + parentMerchantBean.getParentId() + " created by "
				+ parentMerchantBean.getLoginUsername());
			auditLogBean.setGroupId(parentMerchantBean.getLoginRoleId());
			auditLogBean.setUserId(parentMerchantBean.getLoginUserId());
			auditLogBean.setDateTime(currentDateStr);
			auditLogBean.setUserIp(parentMerchantBean.getUserIp());
			auditLogService.createAuditLog(auditLogBean);
		    }
		    entryBeans.add(new EntryBean<String, Serializable>(resultParentMerchantBean.getClass().getName(),
			    resultParentMerchantBean));
		}
	    }
	} catch (Exception e) {
	    log.error(e, e);
	    ApplicationLogBean applicationLogBean = new ApplicationLogBean();
	    applicationLogBean.setType(ApplicationLogBean.TYPE_ERROR);
	    applicationLogBean.setClazz(clazz);
	    applicationLogBean.setMethod(method);
	    applicationLogBean.setMessage(e.getMessage());
	    applicationLogService.createApplicationLog(applicationLogBean);
	}
	return entryBeans;
    }

    @Override
    public ParentMerchantBean getParentMerchant(Long id) {
	ParentMerchantBean parentMerchantBean = null;
	try {
	    ParentMerchant parentMerchant = parentMerchantPersistenceService.findOne(id);
	    if (parentMerchant != null && !ServiceProfile.STATUS_CLOSED.equals(parentMerchant.getParentStatus())) {
		parentMerchantBean = new ParentMerchantBean();
		BeanUtils.copyProperties(parentMerchant, parentMerchantBean);
		parentMerchantBean.setId(parentMerchant.getId());
		Set<MerchantProfile> merchantProfiles = parentMerchant.getMerchantProfiles();
		if (merchantProfiles != null && !merchantProfiles.isEmpty()) {
		    for (MerchantProfile merchantProfile : merchantProfiles) {
			MerchantProfileBean merchantProfileBean = new MerchantProfileBean();
			BeanUtils.copyProperties(merchantProfile, merchantProfileBean);
			merchantProfileBean.setId(merchantProfile.getId());
			parentMerchantBean.getMerchantProfileBeans().add(merchantProfileBean);
		    }
		}
	    }
	} catch (Exception e) {
	    log.error(e.getMessage(), e);
	}
	return parentMerchantBean;
    }

    @Override
    public List<EntryBean<String, Serializable>> updateParentMerchant(ParentMerchantBean parentMerchantBean) {
	String clazz = ParentMerchantService.class.getName();
	String method = "updateParentMerchant";
	List<EntryBean<String, Serializable>> entryBeans = null;
	ParentMerchant parentMerchant = null;
	try {
	    if (parentMerchantBean != null) {
		parentMerchant = parentMerchantPersistenceService.findOne(parentMerchantBean.getId());
		if (parentMerchant != null && isDataChange(parentMerchant, parentMerchantBean)) {

		    ApplicationLogBean applicationLogBean = new ApplicationLogBean();
		    applicationLogBean.setType(ApplicationLogBean.TYPE_INFO);
		    applicationLogBean.setClazz(clazz);
		    applicationLogBean.setMethod(method);
		    applicationLogBean.setMessage(method);
		    applicationLogService.createApplicationLog(applicationLogBean);

		    entryBeans = new ArrayList<EntryBean<String, Serializable>>();
		    // validate
		    List<Error> errors = formValidatorService.validate(clazz, method, parentMerchantBean);
		    List<ErrorBean> errorBeans = new ArrayList<ErrorBean>();
		    if (errors != null && !errors.isEmpty()) {
			for (Error error : errors) {
			    ErrorBean errorBean = new ErrorBean();
			    BeanUtils.copyProperties(error, errorBean);
			    errorBeans.add(errorBean);
			}
		    }
		    if (errorBeans != null && !errorBeans.isEmpty()) {
			entryBeans.add(new EntryBean<String, Serializable>(List.class.getName() + "." + ErrorBean.class.getName(),
				(Serializable) errorBeans));
		    } else {
			Date currentDate = DateUtils.getCurrentDate();
			String currentDateStr = StringUtils.formatDate(currentDate, StringUtils.PATTERN_DATETIME_PERSISTENCE);
			// copy from bean
			BeanUtils.copyProperties(parentMerchantBean, parentMerchant, "id");
			parentMerchant.getMerchantProfiles().clear();
			Set<MerchantProfileBean> merchantProfileBeans = parentMerchantBean.getMerchantProfileBeans();
			if (merchantProfileBeans != null && !merchantProfileBeans.isEmpty()) {
			    for (MerchantProfileBean merchantProfileBean : merchantProfileBeans) {
				MerchantProfile merchantProfile = merchantProfilePersistenceService.findOne(merchantProfileBean.getId());
				merchantProfile.getParentMerchants().clear();
				if (merchantProfile != null) {
				    merchantProfile.getParentMerchants().add(parentMerchant);
				    parentMerchant.getMerchantProfiles().add(merchantProfile);
				}
			    }
			}
			parentMerchant.setLastUpdateBy(parentMerchantBean.getLoginUsername());
			parentMerchant.setLastUpdateDatetime(currentDateStr);
			parentMerchant = parentMerchantPersistenceService.save(parentMerchant);
			ParentMerchantBean resultParentMerchantBean = new ParentMerchantBean();
			BeanUtils.copyProperties(parentMerchant, resultParentMerchantBean);
			resultParentMerchantBean.setId(parentMerchant.getId());

			// audit log
			String logActionType = propertyService.getProperty("auditlog." + clazz + "." + method);
			if (logActionType != null) {
			    AuditLogBean auditLogBean = new AuditLogBean();
			    auditLogBean.setLogActionType(logActionType);
			    auditLogBean.setDescription("Parent ID: " + parentMerchantBean.getParentId() + " updated by "
				    + parentMerchantBean.getLoginUsername());
			    auditLogBean.setGroupId(parentMerchantBean.getLoginRoleId());
			    auditLogBean.setUserId(parentMerchantBean.getLoginUserId());
			    auditLogBean.setDateTime(currentDateStr);
			    auditLogBean.setUserIp(parentMerchantBean.getUserIp());
			    auditLogService.createAuditLog(auditLogBean);
			}
			entryBeans.add(new EntryBean<String, Serializable>(resultParentMerchantBean.getClass().getName(),
				resultParentMerchantBean));
		    }
		} else {
		    entryBeans = new ArrayList<EntryBean<String, Serializable>>();
		    entryBeans.add(new EntryBean<String, Serializable>(parentMerchantBean.getClass().getName(), parentMerchantBean));
		}
	    }
	} catch (Exception e) {
	    log.error(e, e);
	    ApplicationLogBean applicationLogBean = new ApplicationLogBean();
	    applicationLogBean.setType(ApplicationLogBean.TYPE_ERROR);
	    applicationLogBean.setClazz(clazz);
	    applicationLogBean.setMethod(method);
	    applicationLogBean.setMessage(e.getMessage());
	    applicationLogService.createApplicationLog(applicationLogBean);
	}
	return entryBeans;
    }

    @Override
    public List<EntryBean<String, Serializable>> deleteParentMerchant(ParentMerchantBean parentMerchantBean) {
	String clazz = ParentMerchantService.class.getName();
	String method = "deleteParentMerchant";
	List<EntryBean<String, Serializable>> entryBeans = null;
	ParentMerchant parentMerchant = null;
	try {
	    if (parentMerchantBean != null) {
		Date currentDate = DateUtils.getCurrentDate();
		String currentDateStr = StringUtils.formatDate(currentDate, StringUtils.PATTERN_DATETIME_PERSISTENCE);
		entryBeans = new ArrayList<EntryBean<String, Serializable>>();

		// validate
		List<Error> errors = formValidatorService.validate(clazz, method, parentMerchantBean);
		List<ErrorBean> errorBeans = new ArrayList<ErrorBean>();
		if (errors != null && !errors.isEmpty()) {
		    for (Error error : errors) {
			ErrorBean errorBean = new ErrorBean();
			BeanUtils.copyProperties(error, errorBean);
			errorBeans.add(errorBean);
		    }
		} else {
		    Long parentMerchantId = parentMerchantBean.getId();
		    parentMerchant = parentMerchantPersistenceService.findOne(parentMerchantId);
		    if (parentMerchant == null) {
			errorBeans.add(new ErrorBean(
				"errors.com.fw.service.parentmerchant.ParentMerchantService.deleteParentMerchant.notfoundParentMerchant"));
		    }
		}
		// result
		if (errorBeans != null && !errorBeans.isEmpty()) {
		    entryBeans.add(new EntryBean<String, Serializable>(List.class.getName() + "." + ErrorBean.class.getName(),
			    (Serializable) errorBeans));
		} else {

		    // update status
		    parentMerchant.setParentStatus(ParentMerchant.STATUS_INACTIVE);
		    parentMerchant.setLastUpdateBy(parentMerchantBean.getLoginUsername());
		    parentMerchant.setLastUpdateDatetime(currentDateStr);
		    parentMerchant = parentMerchantPersistenceService.save(parentMerchant);

		    ParentMerchantBean resultParentMerchantBean = new ParentMerchantBean();
		    BeanUtils.copyProperties(parentMerchant, resultParentMerchantBean);
		    resultParentMerchantBean.setId(parentMerchant.getId());

		    // audit log
		    String logActionType = propertyService.getProperty("auditlog." + clazz + "." + method);
		    if (logActionType != null) {
			AuditLogBean auditLogBean = new AuditLogBean();
			auditLogBean.setLogActionType(logActionType);
			auditLogBean.setDescription("Parent ID: " + parentMerchant.getParentId() + " deleted by "
				+ parentMerchantBean.getLoginUsername());
			auditLogBean.setGroupId(parentMerchantBean.getLoginRoleId());
			auditLogBean.setUserId(parentMerchantBean.getLoginUserId());
			auditLogBean.setDateTime(currentDateStr);
			auditLogBean.setUserIp(parentMerchantBean.getUserIp());
			auditLogService.createAuditLog(auditLogBean);
		    }
		    entryBeans.add(new EntryBean<String, Serializable>(resultParentMerchantBean.getClass().getName(),
			    resultParentMerchantBean));
		}
	    }
	} catch (Exception e) {
	    log.error(e, e);
	    ApplicationLogBean applicationLogBean = new ApplicationLogBean();
	    applicationLogBean.setType(ApplicationLogBean.TYPE_ERROR);
	    applicationLogBean.setClazz(clazz);
	    applicationLogBean.setMethod(method);
	    applicationLogBean.setMessage(e.getMessage());
	    applicationLogService.createApplicationLog(applicationLogBean);
	}
	return entryBeans;
    }

    @Override
    public Boolean checkUniqueParentId(String parentId) {
	Boolean result = Boolean.FALSE;
	try {
	    List<ParentMerchant> parentMerchants = parentMerchantPersistenceService.selectByParentIdAndStatus(parentId, null);
	    if (parentMerchants == null || parentMerchants.isEmpty()) {
		result = Boolean.TRUE;
	    }
	} catch (Exception e) {
	    log.error(e, e);
	}
	return result;
    }

    @Override
    public SearchResultBean<ParentMerchantBean> selectParentMerchant(SearchCriteriaBean<SearchParentMerchantBean> searchCriteriaBean) {
	SearchResultBean<ParentMerchantBean> searchResultBean = null;
	try {
	    if (searchCriteriaBean != null) {
		SearchParentMerchantBean searchParentMerchantBean = searchCriteriaBean.getCriteriaBean();
		if (searchParentMerchantBean != null) {
		    Page<ParentMerchant> page = parentMerchantPersistenceService.findAll(this.getSpecification(searchParentMerchantBean),
			    PageRequestUtils.getPageRequest(searchCriteriaBean));
		    if (page != null) {
			searchResultBean = new SearchResultBean<ParentMerchantBean>();
			searchResultBean.setTotalElements(page.getTotalElements());
			searchResultBean.setTotalPages(page.getTotalPages());
			List<ParentMerchantBean> content = new ArrayList<ParentMerchantBean>();
			List<ParentMerchant> parentMerchants = page.getContent();
			if (parentMerchants != null && !parentMerchants.isEmpty()) {
			    for (ParentMerchant parentMerchant : parentMerchants) {
				ParentMerchantBean bean = new ParentMerchantBean();
				BeanUtils.copyProperties(parentMerchant, bean);
				bean.setId(parentMerchant.getId());
				content.add(bean);
			    }
			}
			searchResultBean.setContent(content);
		    }
		}
	    }
	} catch (Exception e) {
	    log.error(e.getMessage(), e);
	}
	return searchResultBean;
    }

    private Specification<ParentMerchant> getSpecification(SearchParentMerchantBean searchParentMerchantBean) {
	final String parentId = searchParentMerchantBean.getParentId();
	final String parentName = searchParentMerchantBean.getParentName();
	final String parentContactPerson = searchParentMerchantBean.getParentContactPerson();
	final String parentStatus = searchParentMerchantBean.getParentStatus();

	Specification<ParentMerchant> specification = new Specification<ParentMerchant>() {
	    @Override
	    public Predicate toPredicate(Root<ParentMerchant> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
		List<Predicate> predicates = new ArrayList<Predicate>();
		if (parentId != null && !parentId.isEmpty()) {
		    predicates.add(cb.like(root.<String> get("parentId"), "%" + parentId + "%"));
		}
		if (parentName != null && !parentName.isEmpty()) {
		    predicates.add(cb.like(root.<String> get("parentName"), "%" + parentName + "%"));
		}
		if (parentContactPerson != null && !parentContactPerson.isEmpty()) {
		    predicates.add(cb.or(cb.like(root.<String> get("contactPersonFirstName"), "%" + parentContactPerson + "%"),
			    cb.like(root.<String> get("contactPersonLastName"), "%" + parentContactPerson + "%")
		    // ,cb.like(root.<String> get("contactPersonEmailAddress"), "%" + parentContactPerson + "%")
			    ));
		}
		if (parentStatus != null && !parentStatus.isEmpty()) {
		    predicates.add(cb.equal(root.<String> get("parentStatus"), parentStatus));
		} else {
		    predicates.add(cb.notEqual(root.<String> get("parentStatus"), ParentMerchant.STATUS_CLOSED));
		}

		Predicate result = cb.conjunction();
		for (Predicate predicate : predicates) {
		    result.getExpressions().add(predicate);
		}
		return result;
	    }
	};
	return specification;
    }

    private boolean isDataChange(ParentMerchant parentMerchant, ParentMerchantBean parentMerchantBean) {
	boolean isChange = true;
	// boolean isChange = false;
	// if (parentMerchant != null && parentMerchantBean != null) {
	// if (// service information
	// DataUtils.isChange(parentMerchant.getParentStatus(), parentMerchantBean.getParentStatus())
	// || DataUtils.isChange(parentMerchant.getParentName(), parentMerchantBean.getParentName())) {
	// isChange = true;
	// }
	// }
	return isChange;
    }
}
